package Measurements;

import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Vector;

import au.com.bytecode.opencsv.CSVWriter;
import Basics.*;
import Circuit.Circuit;
import Derivation.SemiNaive;
import Parsing.*;
import Pattern.*;
import TopK.TopKAlgo;

public class OfflineMeasurements 
{
	SemiNaive sn;
	
	Pattern pattern;
	
	Circuit intersectedCircuit;
	
	public OfflineMeasurements () {}

	
	
	/*************************************************************************************************************/
	/** Title: MeasureAndWriteCSV																	
	/** Description: Apply's the function of the class and writes the result in CSV file  						
	/*************************************************************************************************************/
	
	public void MeasureAndWriteCSV (String path) throws IOException
	{
		DB.getInstance().Reset();
		DB.getInstance().DropTableIfExists("imports");
		DB.getInstance().DropTableIfExists("exports");
		DB.getInstance().DropTableIfExists("dealsWith");
		
		FileWriter fw = new FileWriter(path);
		CSVWriter writer = new CSVWriter(fw);
		ArrayList<String[]> data = new ArrayList<String[]>();
		data.add(new String[] {"Size of initial DB", "k",  "Duration of prov. generation in seconds",
				"Duration of intersection in seconds", "Number of derivations stored in initial provenance", "size Of Intersected Circuit", "Size of DB After Iteration", "Time For Top-k"});
		
		writer.writeAll(data);
		DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
		for (int i = 300; i <1000; i+=100) 
		{
			for (int k = 2; k <3; k++)
			{
				DB.getInstance().Reset();
				DB.getInstance().DropTableIfExists("imports");
				DB.getInstance().DropTableIfExists("exports");
				DB.getInstance().DropTableIfExists("dealsWith");
				ParseDB rfDB = new ParseDB ("yago2core_facts.clean.notypes.tsv", i);//C:\\Users\\amirgilad\\Downloads\\
				
				String[] array1 = this.MeasureFullProv();
				String[] array2 = this.MeasureIntersectionWithFullProv(i);
				String[] array3 = this.MeasureTopK(k);
				String[] array1and2and3 = new String[array1.length + array2.length + array3.length + 2];
				System.arraycopy(array1, 0, array1and2and3, 2, array1.length);
				System.arraycopy(array2, 0, array1and2and3, array1.length + 2, array2.length);
				System.arraycopy(array3, 0, array1and2and3, array1.length + array2.length + 2, array3.length);
				array1and2and3[0] = Integer.toString(i);
				array1and2and3[1] = Integer.toString(k);
				writer.writeNext(array1and2and3);
				writer.flush();
				System.out.println("---------------------------------------------------------------------------------------------------------------------------------");
				System.out.println("Last Iteration Finished at: " + dateFormat.format(Calendar.getInstance().getTime()));
				System.out.println(String.format("Size of initial DB: %d\nk: %d\nDuration of prov. generation in seconds: %s\nDuration of intersection in seconds: %s\n" 
					+ "Number of derivations stored in initial provenance: %s\nsize Of Intersected Circuit: %s\nTop-k time: %s\nSize of DB After Iteration: %s",
					i, k, array1[0], array2[0], array2[1], array2[2], array3[0], array2[3]));
				System.out.println("---------------------------------------------------------------------------------------------------------------------------------");
				
				//DB.getInstance().Reset();
			}
		}
		
	    writer.close();
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MeasureFullProv																					
	/** Description: Measures the time and size of Full Prov. via semiNaive									
	/*************************************************************************************************************/
	
	public String[] MeasureFullProv ()
	{
		Program p = MakeProgram ();
		long startTime = System.currentTimeMillis();
		this.sn = new SemiNaive(p);
		sn.Run(false, false);									//with intersection
		long endTime = System.currentTimeMillis();
		double durationFullProv = (endTime - startTime)/1000;
		
		return new String[] {Double.toString(durationFullProv)};
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MeasureIntersectionWithFullProv																					
	/** Description: Measures the time of Intersection With Full Prov. and the size of intersected rules									
	/*************************************************************************************************************/
	
	public String[] MeasureIntersectionWithFullProv (int sizeOfDB)
	{
		pattern = MakePattern();
		IntersectWithProgramOffline iwp = new IntersectWithProgramOffline (sn.getParser().getProv(), this.sn.getParser().getC(), pattern);
		long sizeOfCircuit = this.sn.getParser().GetProvSize();
		long startTime = System.currentTimeMillis();
		//System.out.println("size of prov: " + sn.getParser().GetProvSize());
		int DBSize = DB.getInstance().Size();
		iwp.IntersectNoTransitives(sizeOfDB);
		long endTime = System.currentTimeMillis();
		
		intersectedCircuit = iwp.getC();
		
		double intersectionTime = (endTime - startTime)/1000;
		long sizeOfIntersectedCircuit = iwp.getC().Size();
		
		return new String[] {Double.toString(intersectionTime), Long.toString(sizeOfCircuit), Long.toString(sizeOfIntersectedCircuit), Integer.toString(DBSize)};
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MeasureTopK																					
	/** Description: 									
	/*************************************************************************************************************/
	
	public String[] MeasureTopK (int k)
	{
		TopKAlgo topk = new TopKAlgo();
		long startTime = System.currentTimeMillis();
		//topk.RunTopKForAtom(pattern.getPatternVec().get(0).firstElement(), k, intersectedCircuit.getCircuit());
		topk.RunTopKForPattern(pattern, k, intersectedCircuit.getCircuit());
		long endTime = System.currentTimeMillis();
		double topKTime = (endTime - startTime)/1000;
		//int DBSize = DB.getInstance().Size();
		return new String[] {Double.toString(topKTime)};//, Long.toString(DBSize)
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MakePattern																	
	/** Description: Builds the pattern   						
	/*************************************************************************************************************/
	
	public Pattern MakePattern ()
	{
		////////////////////////////////////////////////
		Constant Canada = new Constant("Canada", "Country");
		Constant Andorra = new Constant("Andorra", "Country");
		PatternNode root = new PatternNode ("dealsWith",false, Canada, Andorra);
		PatternNode child1 = new PatternNode ("dealsWith",false, Andorra, Canada);
		root.setChildren(child1);
		child1.setParent(root); 

		Vector<PatternNode> rootVec = new Vector<PatternNode> ();
		rootVec.add(root);

		Vector<PatternNode> childVec = new Vector<PatternNode> ();
		childVec.add(child1);

		Vector<Vector<PatternNode>> pattern = new Vector<Vector<PatternNode>> ();
		pattern.add(rootVec); 
		pattern.add(childVec); 
		////////////////////////////////////////////////
		
		return new Pattern(pattern);
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: MakeProgram																					
	/** Description: Builds the program out of string									
	/*************************************************************************************************************/
	
	public Program MakeProgram ()
	{
		/*String input = "dealsWith(a, b):- imports(a, c), exports(b, c) & 0.8"
				+ " \n dealsWith(a, b):- dealsWith(a, f), dealsWith(f, b) & 0.5 "
				+ "\n dealsWith(a, b):- dealsWith(b, a) & 1";
		ParseProgram pp = new ParseProgram(input);
		return pp.getProgram();*/
		Var a = new Var("a", "Country");
		Var b = new Var("b", "Country");
		Var c = new Var("c", "Product");
		Var f = new Var("f", "Country");
		
		Atom dealsWith1_1 = new Atom("dealsWith", a,b);
		Atom dealsWith1_2 = new Atom("dealsWith", a,b);
		Atom dealsWith1_3 = new Atom("dealsWith", a,b);
		Atom dealsWith2 = new Atom("dealsWith", a,f);
		Atom dealsWith3 = new Atom("dealsWith", f,b);
		Atom dealsWith4 = new Atom("dealsWith", b,a);
		Atom exported = new Atom("exports", b,c);
		Atom imported = new Atom("imports", a,c);
		
		Rule r1 = new Rule (dealsWith1_1, 0.8, imported, exported);
		Rule r2 = new Rule (dealsWith1_2, 0.5, dealsWith2, dealsWith3);
		Rule r3 = new Rule (dealsWith1_3, 1, dealsWith4);
		return new Program(r1, r2 ,r3);
	}
}
